home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / GUSI / GUSIUDP.cp < prev    next >
Text File  |  1993-12-30  |  11KB  |  420 lines

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIUDP.cp        -    UDP Datagram Sockets
  4. Author    :    Matthias Neeracher
  5.  
  6.     This file was derived from the socket library by 
  7.     
  8.         Charlie Reiman    <creiman@ncsa.uiuc.edu> and
  9.         Tom Milligan    <milligan@madhaus.utcs.utoronto.ca>
  10.           
  11. Started    :    12Aug92                                Language    :    MPW C/C++
  12. Modified    :    16Aug92    Split off
  13.                 25Aug92    MN    Started putting some real work in
  14.                 14Sep92    MN    select() didn't return enough goodies.
  15.                 17Jun93    MN    getsockname() didn't work for some sorts of bind()
  16.                 21Jun93    MN    forgot to reset asyncerr
  17. Last        :    21Jun93
  18. *********************************************************************/
  19.  
  20. #include "GUSIINET_P.h"
  21.  
  22. /********************** Completion procedures ***********************/
  23.  
  24. #pragma segment GUSIResident
  25.  
  26. void udp_read_ahead_done(AnnotatedPB *pb)
  27. {
  28.     UDPSocket *        sock    =    (UDPSocket *) pb->Owner();
  29.     UDPiopb *        udp    =    pb->UDP();
  30.  
  31.     if (!udp->ioResult) {
  32.         sock->recvBuf    = udp->csParam.receive.rcvBuff;
  33.         sock->recvd        = udp->csParam.receive.rcvBuffLen;
  34.     } else {
  35.         sock->recvBuf    = nil;
  36.         sock->recvd        = 0;
  37.     }
  38.     sock->asyncerr = udp->ioResult;
  39. }
  40.  
  41. void udp_send_done(AnnotatedPB *pb)
  42. {    
  43.     UDPiopb *        udp    =    pb->UDP();
  44.     
  45.     ((miniwds *)udp->csParam.send.wdsPtr)->terminus    = udp->ioResult;
  46.     ((miniwds *)udp->csParam.send.wdsPtr)->ptr        = nil;
  47. }
  48.  
  49. #pragma segment GUSIINET
  50.  
  51. /************************ UDPSocket members *************************/
  52.  
  53. UDPSocket::UDPSocket()
  54.     :    INETSocket()
  55. {
  56.     sstate = SOCK_STATE_NO_STREAM;
  57. }
  58.  
  59. UDPSocket::UDPSocket(StreamPtr stream)
  60.     :    INETSocket(stream)
  61. {
  62.     OSErr            err;
  63.     UDPiopb *    pb;
  64.  
  65.     pb                                                = GetPB();
  66.     pb->ioCompletion                            = UDPIOCompletionProc(udp_read_ahead_done);
  67.     pb->csCode                                     = UDPRead;
  68.     pb->csParam.receive.timeOut            = 0 /* infinity */;
  69.     pb->csParam.receive.secondTimeStamp    = 0/* must be zero */;
  70.  
  71.     /* We know that there is a read pending, so we make a synchronous call */
  72.     
  73.     if (err = PBControlSync(ParmBlkPtr(pb)))
  74.         TCP_error(err);
  75.     
  76.     if (!pb->ioResult) {
  77.         recvBuf    = pb->csParam.receive.rcvBuff;
  78.         recvd        = pb->csParam.receive.rcvBuffLen;
  79.     } else {
  80.         recvBuf    = nil;
  81.         recvd        = 0;
  82.     }
  83.     asyncerr                 =    pb->ioResult;
  84.     peer.sin_addr.s_addr    =    pb->csParam.receive.remoteHost;
  85.     peer.sin_port            =    pb->csParam.receive.remotePort;
  86. }
  87.  
  88. UDPSocket::~UDPSocket()
  89. {
  90.     UDPiopb *    pb;
  91.     
  92.     if (sstate == SOCK_STATE_NO_STREAM)
  93.         return;
  94.     
  95.     pb                    = GetPB();    
  96.     pb->csCode         = UDPRelease;
  97.     
  98.     if (!PBControlSync(ParmBlkPtr(pb)))
  99.         DisposPtr(pb->csParam.create.rcvBuff);
  100. }
  101.  
  102. UDPiopb * UDPSocket::GetPB()
  103. {
  104.     AnnotatedPB *    pb        =    INETSockets.GetPB();
  105.     pb->UDP()->ioCRefNum =    INETSockets.Driver();
  106.     pb->UDP()->udpStream    =    stream;
  107.     
  108.     pb->SetOwner(this);
  109.     
  110.     return pb->UDP();
  111. }
  112.  
  113. unsigned long UDPSocket::Available()
  114. {
  115.     return (asyncerr == inProgress) ? 0 : recvd;
  116. }
  117.  
  118. int UDPSocket::NewStream()
  119. {
  120.     OSErr            err;
  121.     UDPiopb *    pb;
  122.  
  123.     pb                                        = GetPB();
  124.     pb->csCode                             = UDPCreate;
  125.     pb->csParam.create.rcvBuff     = (char *)NewPtr(STREAM_BUFFER_SIZE);
  126.     pb->csParam.create.rcvBuffLen    = STREAM_BUFFER_SIZE;
  127.     pb->csParam.create.notifyProc    = NULL;
  128.     pb->csParam.create.localPort     = sa.sin_port;
  129.     
  130.     if (err = PBControlSync(ParmBlkPtr(pb)))
  131.         return TCP_error(err);
  132.         
  133.     stream         = pb->udpStream;
  134.     sa.sin_port = pb->csParam.create.localPort;
  135.     
  136.     sstate     = SOCK_STATE_UNCONNECTED;
  137.     recvd     = 0;
  138.     recvBuf     = 0;
  139.     asyncerr    = inProgress;
  140.  
  141.     return ReadAhead();
  142. }
  143.  
  144. int UDPSocket::FlushReadAhead()
  145. {
  146.     OSErr            err;
  147.     UDPiopb *    pb;
  148.  
  149.     /* flush the read-ahead buffer if its not from our new friend */
  150.     pb                                        = GetPB();
  151.     pb->ioCompletion                    = nil;
  152.     pb->csCode                             = UDPBfrReturn;
  153.     pb->csParam.receive.rcvBuff    = recvBuf;
  154.     recvBuf                                = 0;
  155.     recvd                                   = 0;
  156.     
  157.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  158.         return TCP_error(err);
  159.     else
  160.         return 0;
  161. }
  162.  
  163. int UDPSocket::ReadAhead()
  164. {
  165.     OSErr            err;
  166.     UDPiopb *    pb;
  167.  
  168.     pb                                                = GetPB();
  169.     pb->ioCompletion                            = UDPIOCompletionProc(udp_read_ahead_done);
  170.     pb->csCode                                     = UDPRead;
  171.     pb->csParam.receive.timeOut            = 0 /* infinity */;
  172.     pb->csParam.receive.secondTimeStamp    = 0/* must be zero */;
  173.     asyncerr                                        = 1;
  174.     
  175.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  176.         return TCP_error(asyncerr = err);
  177.     
  178.     return 0;
  179. }
  180.  
  181. /*
  182.  *    getsockname(name, namelen)
  183.  *
  184.  *        getsockname returns the current name for the  socket.
  185.  *        Namelen should  be initialized to
  186.  *        indicate the amount of space pointed to by name.  On  return
  187.  *        it contains the actual size of the name returned (in bytes).
  188.  *        
  189.  *        A 0 is returned if the call succeeds, -1 if it fails.
  190.  */
  191.  
  192. int UDPSocket::getsockname(void *name, int *namelen)
  193. {
  194.     if (sstate == SOCK_STATE_NO_STREAM)
  195.         if (NewStream())
  196.             return -1;
  197.  
  198.     return INETSocket::getsockname(name, namelen);
  199. }
  200.  
  201. /*
  202.  *    UDPSocket::connect - initiate a connection on a MacTCP socket
  203.  *
  204.  *        This  call specifies the address to  which  datagrams
  205.  *        are  to  be  sent, and the only address from which datagrams
  206.  *        are to be received.  
  207.  *             
  208.  *        UDP sockets may use connect() multiple times to change
  209.  *        their association. UDP sockets may dissolve the association
  210.  *        by connecting to an invalid address, such as a null
  211.  *        address.
  212.  *        
  213.  *        If the connection or binding succeeds, then 0 is returned.
  214.  *        Otherwise a -1 is returned, and a more specific error code
  215.  *        is stored in errno.
  216.  *        
  217.  *        EAFNOSUPPORT        The address family in addr is not AF_INET.
  218.  *        
  219.  *        EHOSTUNREACH        The TCP connection came up half-way and 
  220.  *                          then failed.
  221.  */
  222.  
  223. int UDPSocket::connect(void * address, int addrlen)
  224. {
  225.     struct sockaddr_in *    addr    = (struct sockaddr_in *) address;
  226.         
  227.     if (addrlen != sizeof(struct sockaddr_in))
  228.         return GUSI_error(EINVAL);
  229.  
  230.     if (addr->sin_family != AF_INET)
  231.         return GUSI_error(EAFNOSUPPORT);
  232.     
  233.     /* make the stream if its not made already */
  234.     if (sstate == SOCK_STATE_NO_STREAM) {
  235.         if (NewStream())
  236.             return -1;
  237.     } else if (recvBuf)
  238.         if (FlushReadAhead())
  239.             return -1;
  240.     
  241.     /* record our peer */
  242.     peer.sin_len             = sizeof(struct sockaddr_in);
  243.     peer.sin_addr.s_addr    = addr->sin_addr.s_addr;
  244.     peer.sin_port             = addr->sin_port;
  245.     peer.sin_family         = AF_INET;
  246.     sstate                     = SOCK_STATE_CONNECTED;
  247.     
  248.     return 0;
  249. }
  250.  
  251. /*    
  252.  *    UDPSocket::recvfrom(s, buffer, buflen, flags, from, fromlen)
  253.  *        
  254.  *        recv() and recvfrom() attempt to receive a message (ie a datagram) 
  255.  *        on the socket s. 
  256.  *
  257.  *        from returns the address of the socket which sent the message.
  258.  *        fromlen is the usual value-result length parameter. 
  259.  *        
  260.  *        Typically, read() is used with a TCP stream and recv() with
  261.  *        UDP where the idea of a message makes more sense. But in fact,
  262.  *        read() and recv() are equivalent.
  263.  *        
  264.  *        If a message (ie. datagram) is too long to fit in the supplied 
  265.  *        buffer, excess bytes will be discarded..
  266.  *
  267.  *        If no messages are available at the socket, the receive call
  268.  *        waits for a message to arrive, unless the socket is non-
  269.  *        blocking in which case -1 is returned with errno set to 
  270.  *        EWOULDBLOCK.
  271.  *
  272.  *        Flags is ignored.
  273.  *        
  274.  *        If successful, the number of bytes actually received is
  275.  *        returned. Otherwise, a -1 is returned and the global variable
  276.  *        errno is set to indicate the error. 
  277.  *        
  278.  *        ESHUTDOWN    The socket has been shutdown for receive operations.
  279.  */
  280.  
  281. int UDPSocket::recvfrom(void * buffer, int buflen, int, void * from, int * fromlen)
  282. {
  283.     /* make the stream if its not made already */
  284.     if (sstate == SOCK_STATE_NO_STREAM)
  285.         if (NewStream())
  286.             return -1;
  287.             
  288.     /* dont block a non-blocking socket */
  289.     if (nonblocking && asyncerr == 1)
  290.         return GUSI_error(EWOULDBLOCK);
  291.     
  292.     SPIN(asyncerr == 1, SP_DGRAM_READ,0);
  293.  
  294.     if (asyncerr!=noErr)
  295.         return TCP_error(asyncerr);
  296.  
  297.     /* return the data to the user - truncate the packet if necessary */
  298.     buflen = min(buflen,recvd);    
  299.     BlockMove(recvBuf, buffer, buflen);
  300.  
  301.     if (from && *fromlen >= sizeof(struct sockaddr_in)) {
  302.         *(struct sockaddr_in *) from = peer;
  303.         *fromlen = sizeof (struct sockaddr_in);
  304.     }    
  305.     
  306.     /* continue the read-ahead - errors which occur */
  307.     /* here will show up next time around */
  308.     
  309.     FlushReadAhead();
  310.     ReadAhead();
  311.     
  312.     return buflen;
  313. }
  314.  
  315. /*
  316.  *    UDPSocket::sendto(s, buffer, buflen, flags, to, tolen)
  317.  *        sendto() is used to transmit a message to another 
  318.  *        socket on the socket s.
  319.  *
  320.  *        Typically, write() is used with a TCP stream and send() with
  321.  *        UDP where the idea of a message makes more sense. But in fact,
  322.  *        write() and send() are equivalent.
  323.  *
  324.  *    Write() and send() operations are completed as soon as the
  325.  *    data is placed on the transmission queue.
  326.  *
  327.  *        The address of the target is given by to.
  328.  *
  329.  *        The message must be short enough to fit into one datagram.
  330.  *        
  331.  *        Buffer space must be available to hold the message to be 
  332.  *        transmitted, regardless of its non-blocking I/O state.
  333.  *
  334.  *        Flags is ignored.
  335.  *        
  336.  *        These calls return the number of bytes sent, or -1 if an error 
  337.  *        occurred.
  338.  *        
  339.  *        EINVAL           The sum of the iov_len values in the iov array was
  340.  *                                 greater than 65535 (TCP) or 65507 (UDP) or there
  341.  *                       were too many entries in the array (16 for TCP or
  342.  *                       6 for UDP).
  343.  *
  344.  *        ESHUTDOWN        The socket has been shutdown for send operations.
  345.  *        
  346.  *        EMSGSIZE         The message is too big to send in one datagram. (UDP)
  347.  *
  348.  *        ENOBUFS          The transmit queue is full. (UDP)
  349.  */
  350.  
  351. int UDPSocket::sendto(void * buffer, int count, int, void * to, int)
  352. {
  353.     miniwds      awds;
  354.     OSErr        err;
  355.     UDPiopb *    pb;
  356.  
  357.     /* make the stream if its not made already */
  358.     if (sstate == SOCK_STATE_NO_STREAM)
  359.         if (NewStream())
  360.             return -1;
  361.     
  362.     if (count > UDP_MAX_MSG)
  363.         return GUSI_error(EMSGSIZE);
  364.         
  365.     awds.terminus = 0;
  366.     awds.length = count;
  367.     awds.ptr = (char *) buffer;
  368.     
  369.     // if no address passed, hope we have one already in peer field
  370.     if (to == NULL)
  371.         if (peer.sin_len)
  372.             to = &peer;
  373.         else
  374.             return GUSI_error(EHOSTUNREACH);
  375.     
  376.     pb                                        = GetPB();
  377.     pb->ioCompletion                    = UDPIOCompletionProc(udp_send_done);
  378.     pb->csCode                             = UDPWrite;
  379.     pb->csParam.send.remoteHost     = ((struct sockaddr_in *)to)->sin_addr.s_addr;
  380.     pb->csParam.send.remotePort     = ((struct sockaddr_in *)to)->sin_port;
  381.     pb->csParam.send.wdsPtr         = (Ptr)&awds;
  382.     pb->csParam.send.checkSum         = true;
  383.     pb->csParam.send.sendLength     = 0/* must be zero */;
  384.     
  385.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  386.         return TCP_error(err);
  387.     
  388.     // get sneaky. compl. proc sets ptr to nil on completion, and puts result code in
  389.     // terminus field.
  390.     
  391.     SPIN(awds.ptr != NULL, SP_DGRAM_WRITE, count);
  392.     
  393.     if (awds.terminus < 0)
  394.         return TCP_error(awds.terminus);
  395.     else
  396.         return count;
  397. }
  398.  
  399. int UDPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  400. {
  401.     int        goodies     =     0;
  402.     
  403.     if (canRead || canWrite)
  404.         if (sstate == SOCK_STATE_NO_STREAM)
  405.             NewStream();
  406.                 
  407.     if (canRead)
  408.         if (asyncerr != 1) {
  409.             *canRead = true;
  410.             ++goodies;
  411.         }
  412.         
  413.     if (canWrite) {
  414.         *canWrite = true;
  415.         ++goodies;
  416.     }
  417.     
  418.     return goodies;
  419. }
  420.